home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The 640 MEG Shareware Studio 2
/
The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO
/
clang
/
nn.zip
/
NNTP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-12-31
|
18KB
|
771 lines
/*
* nntp module for nn.
*
* The original taken from the nntp 1.5 clientlib.c
* Modified heavily for nn.
*
* Rene' Seindal (seindal@diku.dk) Thu Dec 1 18:41:23 1988
*
* Last change: Fri Jul 7 18:56:00 1989
*/
#include "config.h"
/*
* nn maintains a cache of recently used articles to improve efficiency.
* To change the size of the cache, define NNTPCACHE in config.h to be
* the new size of this cache.
*/
#ifndef NNTPCACHE
#define NNTPCACHE 10
#endif
#ifdef NNTP
#include <stdio.h>
#include "nntp.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
/* This is necessary due to the definitions in m-XXX.h */
#if !defined(NETWORK_DATABASE) || defined(NETWORK_BYTE_ORDER)
#include <netinet/in.h>
#endif
export char nntp_server[256]; /* name of nntp server */
export int use_nntp = 0; /* bool: t iff we use nntp */
export int nntp_failed = 0; /* bool: t iff connection is broken
in nntp_get_article() or nntp_get_active() */
import int silent;
import char news_active[];
import int errno, sys_nerr;
import char *sys_errlist[];
extern int user_error();
extern int sys_error();
#define syserr() (errno >= 0 && errno < sys_nerr ? \
sys_errlist[errno] : "Unknown error.")
import char *mktemp();
static FILE *nntp_in = NULL; /* fp for reading from server */
static FILE *nntp_out = NULL; /* fp for writing to server */
static int is_connected = 0; /* bool: t iff we are connected */
static group_header *group_hd; /* ptr to servers current group */
static int is_set = 0; /* bool: t iff group_hd is set */
static int try_again = 0; /* bool: t if timeout forces retry */
static int can_post = 0; /* bool: t iff NNTP server accepts postings */
#define ERR_TIMEOUT 503 /* Response code for timeout */
#ifdef DEBUG
static FILE *nntp_debug;
/*
* nntp_debug_msg: print a debug message.
*
* The master appends prefix and str to a log file, and clients
* prints it as a message.
*/
nntp_debug_msg(prefix, str)
char *prefix, *str;
{
if (is_master) {
if (nntp_debug != 0) {
fprintf(nntp_debug, "%s %s\n", prefix, str);
fflush(nntp_debug);
}
} else {
msg("%s %s", prefix, str);
fl;
sleep();
}
}
#endif
/*
* nntp_find_server: Find out which host to use as NNTP server.
*
* This is done by consulting the file NNTP_SERVER (defined in
* config.h). Set nntp_server[] to the host's name.
*/
void nntp_find_server()
{
char *cp, *name, *getenv();
char buf[BUFSIZ];
FILE *fp;
/*
* This feature cannot normally be enabled, because the database and
* the users rc file contains references to articles by number, and
* these numbers are not unique across NNTP servers.
*/
#ifdef DEBUG
if ((cp = getenv("NNTPSERVER")) != NULL) {
strncpy(nntp_server, cp, sizeof nntp_server);
return;
}
#endif /* DEBUG */
name = NNTP_SERVER;
if (*name != '/')
name = relative(lib_directory, name);
if ((fp = open_file(name, OPEN_READ)) != NULL) {
while (fgets(buf, sizeof buf, fp) != 0) {
if (*buf == '#' || *buf == '\n')
continue;
if ((cp = strchr(buf, '\n')) != 0)
*cp = '\0';
strncpy(nntp_server, buf, sizeof nntp_server);
fclose(fp);
return;
}
fclose(fp);
}
if (!is_master)
printf("\nCannot find name of NNTP server.\nCheck %s\n", name);
sys_error("Failed to find name of NNTP server!");
}
/*
* nntp_check: Find out whether we need to use NNTP.
*
* This is done by comparing the NNTP servers name with whatever
* gethostname() returns.
* use_nntp and news_active[] are initialised as a side effect.
*/
void nntp_check()
{
char host[128];
char *cp;
nntp_find_server();
gethostname(host, sizeof host);
use_nntp = strcmp(host, nntp_server) != 0; /* too simplistic ??? */
if (use_nntp)
strcpy(news_active, relative(db_directory, "ACTIVE"));
else
strcpy(news_active, NEWS_ACTIVE);
}
/*
* nntp_server_init: initialise a connection to the nntp server.
*
* It expects nntp_server[] to be set previously, by a call to
* nntp_check. It is called from nntp_get_article() and
* nntp_get_active() if there is no established connection.
*/
nntp_server_init()
{
int sockt_rd, sockt_wr;
int response;
char line[NNTP_STRLEN];
if (!is_master && !silent)
msg("Connecting to NNTP server %s ... ", nntp_server);
nntp_failed = 1;
is_connected = 0;
#ifdef DEBUG
if (is_master) {
if (nntp_debug != 0) {
fclose(nntp_debug);
rename("/usr/tmp/nnmaster.log", "/usr/tmp/nnmaster.prev");
}
nntp_debug = fopen("/usr/tmp/nnmaster.log", "a");
} else
nntp_debug = 0;
#endif
sockt_rd = nntp_get_socket();
if (sockt_rd < 0)
return -1;
if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
close(sockt_rd);
return -1;
}
sockt_wr = dup(sockt_rd);
if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
close(sockt_wr);
fclose(nntp_in);
nntp_in = NULL; /* from above */
return -1;
}
/* Now get the server's signon message */
response = nntp_get_server(line, sizeof(line));
if (is_master) {
if (response != OK_CANPOST && response != OK_NOPOST) {
log_entry('N', "Failed to connect to NNTP server");
log_entry('N', "Response: %s", line);
fclose(nntp_out);
fclose(nntp_in);
return -1;
}
} else {
switch (response) {
case OK_CANPOST:
can_post = 1;
break;
case OK_NOPOST:
can_post = 0;
break;
default:
user_error(line);
/* NOTREACHED */
}
}
if (!is_master && !silent)
msg("Connecting to NNTP server %s ... ok (%s)",
nntp_server, can_post ? "posting is allowed" : "no posting");
is_connected = 1;
is_set = 0;
nntp_failed = 0;
try_again = 0;
return 0;
}
/*
* nntp_get_socket: get a connection to the nntp server.
*
* Doesn't return in case of errors.
*/
nntp_get_socket()
{
int s;
struct sockaddr_in sin;
struct servent *getservbyname(), *sp;
struct hostent *gethostbyname(), *hp;
#ifdef h_addr
int x = 0;
register char **cp;
#endif
int (*errfct)() = is_master ? sys_error : user_error;
if ((sp = getservbyname("nntp", "tcp")) == NULL)
(*errfct)("nntp/tcp: Unknown service.\n");
if ((hp = gethostbyname(nntp_server)) == NULL)
(*errfct)("NNTP server %s unknown.\n", nntp_server);
bzero((char *) &sin, sizeof(sin));
sin.sin_family = hp->h_addrtype;
sin.sin_port = sp->s_port;
#ifdef h_addr
/* get a socket and initiate connection -- use multiple addresses */
for (cp = hp->h_addr_list; cp && *cp; cp++) {
s = socket(hp->h_addrtype, SOCK_STREAM, 0);
if (s < 0)
(*errfct)("Can't get NNTP socket: %s\n", syserr());
bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
if (x == 0)
break;
if (!is_master)
msg("Connecting to %s failed: %s", nntp_server, syserr());
(void) close(s);
s = -1;
}
if (x < 0 && !is_master)
(*errfct)("Giving up on NNTP server %s!\n", nntp_server);
#else /* no name server */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
(*errfct)("Can't get NNTP socket: %s\n", syserr());
/* And then connect */
bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
if (is_master)
(*errfct)("Connecting to %s failed: %s", nntp_server, syserr());
s = -1;
}
#endif
return s;
}
/*
* nntp_put_server: send a line to the nntp server.
*
* Expects to be connected to the server.
*/
nntp_put_server(string)
char *string;
{
#ifdef DEBUG
nntp_debug_msg(">>>", string);
#endif
fprintf(nntp_out, "%s\r\n", string);
if (fflush(nntp_out) == EOF) {
nntp_io_error();
return -1;
}
return 0;
}
/*
* nntp_get_server_line: get a line from the server.
*
* Expects to be connected to the server.
* The line can be any kind of line, i.e., either response or text.
*/
nntp_get_server_line(string, size)
char *string;
int size;
{
register char *cp, *nl;
if (fgets(string, size, nntp_in) == NULL) {
nntp_io_error();
return -1;
}
for (cp = string, nl = NULL; *cp != NUL; cp++) {
if (*cp == CR) {
nl = cp;
break;
}
if (nl == NULL && *cp == NL)
nl = cp;
}
if (nl != NULL) *nl = NUL;
return 0;
}
/*
* nntp_get_server: get a response line from the server.
*
* Expects to be connected to the server.
* Returns the numerical value of the reponse, or -1 in case of errors.
*/
nntp_get_server(string, size)
char *string;
int size;
{
if (nntp_get_server_line(string, size) < 0)
return -1;
#ifdef DEBUG
nntp_debug_msg("<<<", string);
#endif
return isdigit(*string) ? atoi(string) : 0;
}
/*
* nntp_ask_server: ask the server a question and return the answer.
*
* Expects to be connected to the server.
* Returns the numerical value of the reponse, or -1 in case of
* errors.
* Contains some code to handle server timeouts intelligently.
*/
nntp_ask_server(string, size)
char *string;
int size;
{
int response;
if (nntp_put_server(string) < 0)
return -1;
response = nntp_get_server(string, size);
/*
* Handle the response from the server. Responses are handled as
* followes:
*
* 100-199 Informational. Passed back. (should they be ignored?).
* 200-299 Ok messages. Passed back.
* 300-399 Ok and proceed. Can not happen in nn.
* 400-499 Errors (no article, etc). Passed up and handled there.
* 500-599 Fatal NNTP errors. Handled below.
*/
if (response == ERR_GOODBYE || response > ERR_COMMAND) {
nntp_failed = 1;
nntp_close_server();
if (response != ERR_TIMEOUT) { /* if not timeout, complain */
if (is_master)
sys_error("NNTP botch: internal error: %s", string);
else
user_error("NNTP botch: internal error: %s", string);
/* NOTREACHED */
}
try_again = 1;
is_set = 0;
}
return response;
}
/*
* nntp_close_server: close the connection to the server.
*/
nntp_close_server()
{
if (!is_connected)
return;
if (!nntp_failed) { /* avoid infinite recursion */
char line[NNTP_STRLEN];
int n;
strcpy(line, "QUIT");
n = nntp_ask_server(line, sizeof line);
if (n != OK_GOODBYE)
; /* WHAT NOW ??? */
}
(void) fclose(nntp_out);
(void) fclose(nntp_in);
is_connected = 0;
}
/*
* nntp_io_error: signal an I/O error in talking to the server.
*
* An nn client terminates a session with the user. The master
* simply closes the connection. The flag nntp_failed is set, for
* use by the master to terminate collection.
*
* BUG: if the nntp server is forcibly killed, errno can contain a
* bogus value, resulting in strange error messages. It is
* probably better just to write out the numerical value of errno.
*/
nntp_io_error()
{
nntp_failed = 1;
if (is_master) {
if (is_connected) {
log_entry('N', "Lost connection to server %s: %s", nntp_server, syserr());
nntp_close_server();
}
} else {
user_error("Lost connection to NNTP server %s: %s", nntp_server, syserr());
/* NOTREACHED */
}
}
/*
* nntp_no_post: Check to see whether posting is allowed.
*/
nntp_no_post()
{
if (!is_connected && nntp_server_init() < 0)
return 1; /* If we cannot connect, neither can inews */
if (can_post == 0) {
msg("NNTP server does not allow postings from this host. Sorry!");
return 1;
}
return 0;
}
/*
* nntp_copy_text: copy text response into file.
*
* Sends COMMAND to the server, and copies the following text response
* into an open file.
* Return -1 on error, 0 otherwise. It is treated as an error, if
* the returned response it not what was expected.
*/
nntp_copy_text(fp, command, expected)
FILE *fp;
char *command;
int expected;
{
char buf[NNTP_STRLEN];
char *cp;
int n, nlines;
strcpy(buf, command);
if ((n = nntp_ask_server(buf, sizeof buf)) < 0) {
return -1;
}
if (n != expected)
return -1;
nlines = 0;
while ((n = nntp_get_server_line(buf, sizeof buf)) >= 0) {
cp = buf;
if (*cp == '.')
if (*++cp == '\0')
return nlines > 0 ? 0 : -1;
fputs(cp, fp);
putc('\n', fp);
nlines++;
}
return -1;
}
/*
* nntp_get_active: get a copy of the active file.
*
* If we are the master get a copy of the file from the nntp server.
* nnadmin just uses the one we already got. In this way the master
* can maintain a remote copy of the servers active file.
* We try to be a little smart, if not inefficient, about the
* modification times on the local active file.
* Even when the master is running on the nntp server, a separate
* copy of the active file will be made for access via NFS.
*/
nntp_get_active()
{
FILE *old, *new;
char bufo[NNTP_STRLEN], bufn[NNTP_STRLEN];
char *new_name;
int same;
if (!is_master)
return access(news_active, 4);
again:
if (!is_connected && nntp_server_init() < 0)
return -1;
new_name = mktemp(relative(db_directory, ".actXXXXXX"));
if ((new = fopen(new_name, "w+")) == NULL)
return -1;
if (nntp_copy_text(new, "LIST", OK_GROUPS) < 0) {
fclose(new);
unlink(new_name);
if (try_again) /* Handle nntp server timeouts */
goto again;
else
return -1;
}
rewind(new);
same = 0;
if ((old = open_file(news_active, OPEN_READ)) != NULL) {
do {
fgets(bufo, sizeof bufo, old);
fgets(bufn, sizeof bufn, new);
} while (!feof(old) && !feof(new) && strcmp(bufo, bufn) == 0);
same = feof(old) && feof(new);
fclose(old);
}
fclose(new);
if (same)
unlink(new_name);
else
if (rename(new_name, news_active) != 0)
sys_error("Cannot rename %s to %s", new_name, news_active);
return 0;
}
/*
* The following functions implements a simple lru cache of recently
* accessed articles. It is a simple way to improve effeciency. Files
* must be kept by name, because the rest of the code expects to be able
* to open an article multiple times, and get separate file pointers.
*/
struct cache {
char *file_name; /* file name */
article_number art; /* article stored in file */
group_header *grp; /* from this group */
unsigned time; /* time last accessed */
} cache[NNTPCACHE];
static unsigned cur_time = 1; /* virtual time */
/*
* nntp_search_cache: search the cache for an (article, group) pair.
*
* Returns a pointer to the slot where it is, null otherwise
*/
struct cache *nntp_search_cache(art, gh)
article_number art;
group_header *gh;
{
struct cache *cptr = cache;
int i;
for (i = 0; i < NNTPCACHE; i++, cptr++)
if (cptr->art == art && cptr->grp == gh) {
cptr->time = cur_time++;
return cptr;
}
return NULL;
}
/*
* nntp_new_slot: get a free cache slot.
*
* Returns a pointer to the allocated slot.
* Frees the old filename, and allocates a new, unused filename.
* The user's files are in ~/.nn, and the master's are in LIB_DIRECTORY.
*/
struct cache *nntp_new_slot()
{
struct cache *cptr = cache;
int i, lru;
unsigned min_time = cur_time;
char name[24];
for (i = 0; i < NNTPCACHE; i++, cptr++)
if (min_time > cptr->time) {
min_time = cptr->time;
lru = i;
}
cptr = &cache[lru];
if (cptr->file_name) {
unlink(cptr->file_name);
free(cptr->file_name);
}
sprintf(name, "nn%02d-XXXXXX", lru);
cptr->file_name =
copy_str(relative(is_master ? lib_directory : nn_directory,
mktemp(name)));
cptr->time = cur_time++;
return cptr;
}
/*
* nntp_clean_cache: clean up the cache.
*
* Removes all allocated files.
*/
void nntp_clean_cache()
{
struct cache *cptr = cache;
int i;
for (i = 0; i < NNTPCACHE; i++, cptr++)
if (cptr->file_name)
unlink(cptr->file_name);
}
/*
* nntp_set_group: set the server's current group.
*
* Actual communication is delayed until an article is accessed, to
* avoid unnecessary traffic.
*/
nntp_set_group(gh)
group_header *gh;
{
group_hd = gh;
is_set = 0;
return 0;
}
/*
* nntp_get_article: get an article from the server.
*
* Returns a FILE pointer.
* If necessary the server's current group is set.
* The article (header and body) are copied into a file, so they
* are seekable (nn likes that).
*/
FILE *nntp_get_article(article)
article_number article;
{
char buf[NNTP_STRLEN];
FILE *tmp;
struct cache *cptr;
int n;
again:
if (!is_connected && nntp_server_init() < 0) {
return NULL;
}
/*
* Set the server group to the current group
*/
if (is_set == 0) {
sprintf(buf, "GROUP %s", group_hd->group_name);
if ((n = nntp_ask_server(buf, sizeof buf)) < 0) {
return NULL;
}
if (n != OK_GROUP) {
if (try_again)
goto again; /* Handle nntp server timeouts */
else
return NULL;
}
is_set = 1;
}
/*
* Search the cache for the requested article, and allocate a new
* slot if necessary.
*/
cptr = nntp_search_cache(article, group_hd);
if (cptr != 0) {
return open_file(cptr->file_name, OPEN_READ);
}
cptr = nntp_new_slot();
if ((tmp = open_file(cptr->file_name, OPEN_CREATE)) == NULL)
return NULL;
/*
* Copy the article.
*/
sprintf(buf, "ARTICLE %d", article);
if (nntp_copy_text(tmp, buf, OK_ARTICLE) < 0) {
fclose(tmp);
if (try_again)
goto again; /* Handle nntp server timeouts */
else
return NULL;
}
fclose(tmp);
if ((tmp = open_file(cptr->file_name, OPEN_READ)) != NULL) {
cptr->art = article;
cptr->grp = group_hd;
}
return tmp;
}
/*
* nntp_cleanup: clean up after an nntp session.
*
* Called from nn_exit().
*/
nntp_cleanup()
{
if (is_connected)
nntp_close_server();
nntp_clean_cache();
}
#endif /* NNTP */